<!doctype html>
<html lang="zh">
<head>
  <meta charset="utf-8">
  <title>加州房价预测 Demo</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
    body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; margin: 20px; }
    h1 { margin-bottom: 0.2rem; }
    .grid { display: grid; grid-template-columns: repeat(2, minmax(240px, 1fr)); gap: 12px; }
    .card { border: 1px solid #ddd; border-radius: 8px; padding: 12px; }
    label { display: block; font-size: 0.9rem; margin-bottom: 4px; }
    input[type="number"], input[type="text"] { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 6px; }
    button { padding: 8px 14px; border: none; background: #3b82f6; color: white; border-radius: 6px; cursor: pointer; }
    button:disabled { background: #9ca3af; cursor: not-allowed; }
    .muted { color: #666; font-size: 0.9rem; }
    img.plot { width: 100%; max-width: 520px; border: 1px solid #eee; border-radius: 6px; }
    .row { display: flex; gap: 16px; flex-wrap: wrap; }
    .kpi { font-size: 0.95rem; }
  </style>
</head>
<body>
  <h1>加州房价预测</h1>
  <p class="muted">基于 scikit-learn 的 HistGradientBoostingRegressor 模型。先运行 <code>python train.py</code> 训练并生成模型与图表，再启动 <code>python app.py</code>。</p>

  <div class="row">
    <div class="card" style="flex: 1 1 340px;">
      <h3>单条预测（表单）</h3>
      <form id="form">
        <div class="grid">
          <!-- 动态生成输入 -->
          {% for name in feature_names %}
          <div>
            <label for="{{name}}">{{name}}</label>
            <input type="number" step="any" id="{{name}}" name="{{name}}" required>
          </div>
          {% endfor %}
        </div>
        <div style="margin-top: 12px;">
          <button type="submit">预测</button>
          <span id="pred" class="kpi"></span>
        </div>
      </form>
      <p class="muted">或使用 JSON POST /predict（对象或对象数组）以及 /predict-csv 上传 CSV。</p>
      <pre class="muted">curl -X POST http://localhost:5000/predict -H "Content-Type: application/json" -d @models/sample_input.json</pre>
    </div>

    <div class="card" style="flex: 1 1 340px;">
      <h3>评估指标</h3>
      {% if metrics.metrics %}
        <div class="kpi">RMSE: {{metrics.metrics.RMSE|round(4)}}</div>
        <div class="kpi">MAE: {{metrics.metrics.MAE|round(4)}}</div>
        <div class="kpi">R²: {{metrics.metrics.R2|round(4)}}</div>
        <div class="muted">训练时间: {{metrics.timestamp}}</div>
      {% else %}
        <div class="muted">尚未训练，请先运行 python train.py</div>
      {% endif %}
    </div>
  </div>

  <div class="row" style="margin-top: 16px;">
    <div class="card">
      <h3>预测 vs 真实</h3>
      <img class="plot" src="/plots/pred_vs_true.png" alt="pred vs true">
    </div>
    <div class="card">
      <h3>残差分布</h3>
      <img class="plot" src="/plots/residuals.png" alt="residuals">
    </div>
    <div class="card">
      <h3>特征重要性</h3>
      <img class="plot" src="/plots/feature_importance.png" alt="feature importance">
    </div>
  </div>

  <script>
    const form = document.getElementById('form');
    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      const pred = document.getElementById('pred');
      pred.textContent = '预测中...';
      const data = {};
      new FormData(form).forEach((v, k) => data[k] = v === '' ? null : Number(v));
      try {
        const r = await fetch('/predict', {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify(data)
        });
        const j = await r.json();
        if (!r.ok) throw new Error(j.error || '请求失败');
        pred.textContent = '预测(单位: 10万美元): ' + j.predictions[0].toFixed(4);
      } catch (err) {
        pred.textContent = '错误: ' + err.message;
      }
    });
  </script>
</body>
</html>